package com.stars.easyms.alarm.client.dingtalk;

import com.stars.easyms.alarm.client.BaseEasyMsAlarmClient;
import com.stars.easyms.alarm.client.EasyMsAlarmChannel;
import com.stars.easyms.alarm.client.dingtalk.message.DingTalkRobotResponse;
import com.stars.easyms.alarm.client.dingtalk.message.EasyMsDingTalkRobotMarkdownMessage;
import com.stars.easyms.alarm.constant.EasyMsAlarmConstant;
import com.stars.easyms.alarm.message.EasyMsAlarmMessage;
import com.stars.easyms.alarm.client.dingtalk.message.EasyMsDingTalkRobotTextMessage;
import com.stars.easyms.alarm.exception.EasyMsAlarmRuntimeException;
import com.stars.easyms.alarm.message.EasyMsAlarmMessageType;
import com.stars.easyms.alarm.util.EasyMsAlarmUtil;
import com.stars.easyms.base.annotation.Transient;
import com.stars.easyms.base.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.ResponseEntity;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * <p>className: EasyMsDingTalkRobotClient</p>
 * <p>description: EasyMs钉钉机器人客户端</p>
 *
 * @author guoguifang
 * @version 1.6.1
 * @date 2020/8/24 11:27 上午
 */
@Slf4j
public final class EasyMsDingTalkAlarmClient extends BaseEasyMsAlarmClient {

    private static final String ALGORITHM = "HmacSHA256";

    private static final String NEW_LINE = "\n";

    @Transient
    private Mac mac;

    private String url;

    @Transient
    private String secret;

    EasyMsDingTalkAlarmClient(EasyMsAlarmChannel easyMsAlarmChannel, String id, String url, String secret) {
        super(easyMsAlarmChannel, id);
        this.url = url;
        this.secret = secret;
        this.init();
    }

    EasyMsDingTalkAlarmClient(EasyMsAlarmChannel easyMsAlarmChannel, String url, String secret) {
        this(easyMsAlarmChannel, null, url, secret);
    }

    /**
     * 发送告警信息，使用markdown格式
     *
     * @param alarmMessage 告警信息
     * @throws Exception 发送异常
     */
    @Override
    public void sendAlarmMessage(EasyMsAlarmMessage alarmMessage) {
        if (EasyMsAlarmMessageType.MARKDOWN == alarmMessage.getType()) {
            sendMarkdownAlarmMessage(alarmMessage);
        } else {
            sendTextAlarmMessage(alarmMessage);
        }
    }

    /**
     * 根据url和secret获取签名并生成webHook地址
     */
    private String getWebHook() throws Exception {
        long timestamp = System.currentTimeMillis();
        return url + "&timestamp=" + timestamp + "&sign=" + getSign(timestamp);
    }

    /**
     * 获取签名
     *
     * @param timestamp 当前时间戳，毫秒级单位
     * @return 签名信息
     */
    private String getSign(long timestamp) throws Exception {
        String sign = timestamp + NEW_LINE + secret;
        return URLEncoder.encode(new String(Base64.encodeBase64(
                mac.doFinal(sign.getBytes(StandardCharsets.UTF_8)))),
                StandardCharsets.UTF_8.name());
    }

    /**
     * 发送text格式的告警信息
     *
     * @param alarmMessage 告警信息
     * @throws Exception 发送异常
     */
    private void sendTextAlarmMessage(EasyMsAlarmMessage alarmMessage) {
        EasyMsDingTalkRobotTextMessage dingTalkMessage = new EasyMsDingTalkRobotTextMessage();
        String context = alarmMessage.getContext();
        List<String> atMobiles = alarmMessage.getAtMobiles();
        if (!atMobiles.isEmpty()) {
            dingTalkMessage.setAtMobiles(atMobiles);
            StringBuilder stringBuilder = new StringBuilder(context);
            stringBuilder.append(NEW_LINE);
            for (String atMobile : atMobiles) {
                stringBuilder.append("@").append(atMobile).append(" ");
            }
            dingTalkMessage.setContent(stringBuilder.toString());
        } else {
            dingTalkMessage.setContent(context);
        }
        dingTalkMessage.setAtAll(alarmMessage.getAtAll());
        send(dingTalkMessage);
    }

    /**
     * 发送markdown格式的告警信息
     *
     * @param alarmMessage 告警信息
     * @throws Exception 发送异常
     */
    private void sendMarkdownAlarmMessage(EasyMsAlarmMessage alarmMessage) {
        EasyMsDingTalkRobotMarkdownMessage dingTalkMessage = new EasyMsDingTalkRobotMarkdownMessage();
        String title = alarmMessage.getTitle();
        if (StringUtils.isBlank(title)) {
            title = EasyMsAlarmConstant.DEFAULT_TITLE;
        }
        dingTalkMessage.setTitle(title);
        String text = alarmMessage.getContext();
        List<String> atMobiles = alarmMessage.getAtMobiles();
        if (!atMobiles.isEmpty()) {
            dingTalkMessage.setAtMobiles(atMobiles);
            StringBuilder stringBuilder = new StringBuilder(text);
            stringBuilder.append(NEW_LINE).append(NEW_LINE);
            for (String atMobile : atMobiles) {
                stringBuilder.append("@").append(atMobile).append(" ");
            }
            dingTalkMessage.setText(stringBuilder.toString());
        } else {
            dingTalkMessage.setText(text);
        }
        dingTalkMessage.setAtAll(alarmMessage.getAtAll());
        send(dingTalkMessage);
    }

    /**
     * 发送消息
     */
    private void send(Object message) {
        ResponseEntity<DingTalkRobotResponse> response;
        try {
            response = EasyMsAlarmUtil.sendAlarmMessage(getWebHook(), message, DingTalkRobotResponse.class);
        } catch (Exception e) {
            log.error("发送消息到钉钉机器人(id:{})失败！消息内容: {}.", getClientId(), JsonUtil.toJSONString(message), e);
            return;
        }
        // 经测试得出当消息发送速度过快时由于钉钉限流策略，每分钟发送大于20次时可能会出现response为null的情况
        if (response.getBody() == null || !Integer.valueOf(0).equals(response.getBody().getErrcode())) {
            log.error("发送消息到钉钉机器人(id:{})失败！消息内容: {}，失败原因: {}",
                    getClientId(), JsonUtil.toJSONString(message),
                    response.getBody() == null ? "响应超时" : response.getBody().getErrmsg());
        }
    }

    private void init() {
        try {
            mac = Mac.getInstance(ALGORITHM);
            mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), ALGORITHM));
        } catch (Exception e) {
            throw new EasyMsAlarmRuntimeException("初始化{}(id:{})失败！", this.getClass().getSimpleName(), getClientId(), e);
        }
    }

}
